package com.ryx.service.Impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ryx.constants.Constant;
import com.ryx.entity.SysDept;
import com.ryx.entity.SysUser;
import com.ryx.exception.GlobalException;
import com.ryx.exception.code.BaseResponseCode;
import com.ryx.mapper.SysDeptMapper;
import com.ryx.mapper.SysUserMapper;
import com.ryx.service.PermissionService;
import com.ryx.service.RoleService;
import com.ryx.service.UserRoleService;
import com.ryx.service.UserService;
import com.ryx.utils.*;
import com.ryx.vo.req.*;
import com.ryx.vo.resp.LoginRespVo;
import com.ryx.vo.resp.PageVo;
import com.ryx.vo.resp.UserOwnRoleRespVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @BelongsPackage: com.ryx.service.Impl
 * @Author: 容永轩
 * @CreateTime: 2020-11-07
 * @Description:
 */
@Service
@Transactional
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private SysUserMapper userMapper;

    @Autowired
    private SysDeptMapper sysDeptMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;

    //token刷新类
    @Autowired
    private TokenSetting tokenSetting;


    @Override
    public LoginRespVo login(LoginReqVo vo) {
        SysUser user = userMapper.selectByUsername(vo.getUsername());

        if (user == null) {
            throw new GlobalException(BaseResponseCode.ACCOUNT_ERROR);
        }

        if (user.getStatus() == 2) {
            throw new GlobalException(BaseResponseCode.ACCOUNT_LOCK_TIP);
        }

        if (!PasswordUtils.matches(user.getSalt(), vo.getPassword(), user.getPassword())) {
            throw new GlobalException(BaseResponseCode.ACCOUNT_PASSWORD_ERROR);
        }

        Map<String, Object> cliamsMap = new HashMap<>();
        cliamsMap.put(Constant.JWT_USER_NAME, user.getUsername());
        cliamsMap.put(Constant.ROLES_INFOS_KEY, getRoleByUserId(user.getId()));
        cliamsMap.put(Constant.PERMISSIONS_INFOS_KEY, getPermissionByUserId(user.getId()));

        //生成token  通过jwt工具类
        String accessToken = JWTTokenUtil.getAccessToken(user.getId(), cliamsMap);
        log.info("accessToken:{}", accessToken);

        //refresh token
        Map<String, Object> refreshCliamsMap = new HashMap<>();
        refreshCliamsMap.put(Constant.JWT_USER_NAME, user.getUsername());
        String refreshToken = null;

        if (vo.getType().equals("1")) {
            refreshToken = JWTTokenUtil.getRefreshToken(user.getId(), refreshCliamsMap);
        } else {
            refreshToken = JWTTokenUtil.getRefreshAppToken(user.getId(), refreshCliamsMap);
        }

        log.info("refreshToken:{}", refreshToken);
        LoginRespVo loginResp = new LoginRespVo();

        loginResp.setAccessToken(accessToken);
        loginResp.setRefreshToken(refreshToken);
        loginResp.setUserId(user.getId());
        return loginResp;
    }

    @Override
    public PageVo<SysUser> pageInfo(UserPageReqVo vo) {

        PageHelper.startPage(vo.getPageNum(), vo.getPageSize());

        List<SysUser> list = userMapper.selectAll(vo);

        //修改分页接口加上所属部门字段
        for (SysUser sysUser : list) {
            SysDept sysDept = sysDeptMapper.selectByPrimaryKey(sysUser.getDeptId());
            if (sysDept != null) {
                sysUser.setDeptName(sysDept.getName());
            }
        }
        return PageUtil.getPageVo(list);


//        //接着要紧跟我们的查询语句
//        List<SysUser> list = userMapper.selectAll();
//
//        //响应数据
////        PageInfo<SysUser> pageInfo = new PageInfo<>(list);
//        PageVo pageVo = PageUtil.getPageVo(list);
//        return pageVo;
    }

    @Override
    public void addUser(UserAddReqVo vo) {
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(vo, sysUser);
        String salt = PasswordUtils.getSalt();

        String encode = PasswordUtils.encode(vo.getPassword(), salt);

        sysUser.setSalt(salt);
        sysUser.setPassword(encode);
        sysUser.setId(UUID.randomUUID().toString());
        sysUser.setCreateTime(new Date());


        int i = userMapper.insertSelective(sysUser);
        if (i != 1) {
            throw new GlobalException(BaseResponseCode.OPERATION_ERROR);
        }


    }

    @Override
    public void logout(String accessToken, String refreshToken) {
        if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
            throw new GlobalException(BaseResponseCode.DATA_ERROR);
        }

        //首先要调用 subject.logout(); 这个主要是清空 shiro 的一些缓存信息，
        // 然后就是我们系统具体业务了 把 access_token 加入黑名单、refresh_token 加入黑名单。
        Subject subject = SecurityUtils.getSubject();
        if (subject != null) {
            subject.logout();
        }
        String userId = JWTTokenUtil.getUserId(accessToken);
        /**
         * 把accessToken 加入黑名单   JWTTokenUtil.getRemainingTime(accessToken)获取剩余时间，毫秒值
     */
        redisUtil.set(Constant.JWT_ACCESS_TOKEN_BLACKLIST + accessToken, userId, JWTTokenUtil.getRemainingTime(accessToken), TimeUnit.MILLISECONDS);

        /**
         * 把refreshToken 加入黑名单
         */
        redisUtil.set(Constant.JWT_REFRESH_IDENTIFICATION + refreshToken, userId, JWTTokenUtil.getRemainingTime(refreshToken), TimeUnit.MILLISECONDS);
    }

    //这个是把数据全部都set进去我们的响应类中，然后返回
    @Override
    public UserOwnRoleRespVo getUserOwnRole(String userId) {

        UserOwnRoleRespVo respVo = new UserOwnRoleRespVo();
        respVo.setOwnRoles(userRoleService.getRoleIdsByUserId(userId));
        respVo.setAllRole(roleService.selectAll());
        return respVo;
    }

    //这个是调用 把信息加入关联表中 关联表的操作
    // 最后也是要放到这个service中而不是在controller中直接用
    @Override
    public void setUserOwnRole(UserOwnRoleReqVo vo) {
        userRoleService.addUserRoleInfo(vo);

        /**
         * 标记用户 要主动去刷新
         */
        redisUtil.set(Constant.JWT_REFRESH_KEY + vo.getUserId(), vo.getUserId(), tokenSetting.getAccessTokenExpireTime().toMillis(), TimeUnit.MILLISECONDS);
        /**
         * 清楚用户授权数据缓存
         */
        redisUtil.delete(Constant.IDENTIFY_CACHE_KEY + vo.getUserId());

    }

    //通过id查询用户
    @Override
    public SysUser selectUserById(String userId) {

        return userMapper.selectByPrimaryKey(userId);
    }

    //刷新token
    @Override
    public String refreshToken(String refreshToken) {
        //是否过期
        //是否加入黑名单
        //redisUtil.hasKey(Constant.JWT_REFRESH_TOKEN_BLACKLIST + refreshToken 这段是如果拿着之前登出的token进行刷新就直接报错
        if (!JWTTokenUtil.validateToken(refreshToken) || redisUtil.hasKey(Constant.JWT_REFRESH_TOKEN_BLACKLIST + refreshToken)) {
            throw new GlobalException(BaseResponseCode.TOKEN_ERROR);
        }

        String userId = JWTTokenUtil.getUserId(refreshToken);
        String username = JWTTokenUtil.getUserName(refreshToken);

//        log.info("userId={}", userId);

        Map<String, Object> map = new HashMap<>();
        map.put(Constant.ROLES_INFOS_KEY, getRoleByUserId(userId));
        map.put(Constant.PERMISSIONS_INFOS_KEY, getPermissionByUserId(userId));
        map.put(Constant.JWT_USER_NAME, username);


        return JWTTokenUtil.getAccessToken(userId, map);
    }

    @Override
    public void updateUserInfo(UserUpdateReqVo vo, String operationId) {

        //拿到当前要执行修改操作的user
        SysUser sysUser = userMapper.selectByPrimaryKey(vo.getId());
        if (null == sysUser) {
            throw new GlobalException(BaseResponseCode.DATA_ERROR);
        }

        //把表单的数据赋值给 通过id查询出来的数据 覆盖
        BeanUtils.copyProperties(vo, sysUser);
        sysUser.setCreateTime(new Date());
        if (!StringUtils.isEmpty(vo.getPassword())) {
            String newPass = PasswordUtils.encode(vo.getPassword(), PasswordUtils.getSalt());
            sysUser.setPassword(newPass);
        } else {
            sysUser.setPassword(null);
        }

        sysUser.setUpdateId(operationId);

        int i = userMapper.updateByPrimaryKey(sysUser);

        if (i != 1) {
            throw new GlobalException(BaseResponseCode.OPERATION_ERROR);
        }

        /*
             * 说明用户状态有改变
             * 加入变成禁用，之前
             * 签发的token 都要失效
         */
        if (vo.getStatus() == 2) {
            redisUtil.set(Constant.ACCOUNT_LOCK_KEY + sysUser.getId(), sysUser.getId());
        } else {
            redisUtil.delete(Constant.ACCOUNT_LOCK_KEY + sysUser.getId());
        }
    }

    @Override
    public void deleteUsers(List<String> userIds, String operationId) {

        SysUser sysUser = new SysUser();

        sysUser.setUpdateId(operationId);
        sysUser.setUpdateTime(new Date());
        sysUser.setDeleted(0);
        int i = userMapper.deletedUsers(sysUser, userIds);
        if (i == 0) {
            throw new GlobalException(BaseResponseCode.OPERATION_ERROR);
        }
        /*
         * 标记用户id 已删除
         * 因为我们是以签发token 的形式保持用户登录状态的
         * 有可能签发了多次toekn 所以在用户删除的时候
         * 要把userId标记起来 过期时间为refreahToekn 的过期时间
         * 避免它可以通过刷新toekn来继续保持登录
      */
        for (String userId : userIds) {
            //标记  选用getRefreshTokenExpireAppTime 是因为app过期时间最长
            redisUtil.set(Constant.DELETED_USER_KEY + userId, userId, tokenSetting.getRefreshTokenExpireAppTime().toMillis(), TimeUnit.MILLISECONDS);
            /**
             * 清楚用户授权数据缓存
             */
            redisUtil.delete(Constant.IDENTIFY_CACHE_KEY + userId);
        }


    }

    //调用mapper中的删除方法
    @Override
    public List<SysUser> selectUserInfoByDeptIds(List<String> deptIds) {
        return  userMapper.selectUserInfoByDeptIds(deptIds);
    }

    /*
    userId 是当前操作的用户id
     */
    @Override
    public void userUpdateDetailInfo(UserUpdateDetailInfoReqVo vo, String userId) {

        SysUser sysUser = new SysUser();

        BeanUtils.copyProperties(vo,sysUser);

        sysUser.setUpdateTime(new Date());
        sysUser.setUpdateId(userId);
        //因为是操作自己的信息
        sysUser.setId(userId);


        int i = userMapper.updateByPrimaryKeySelective(sysUser);
        if (i !=1){
            throw new GlobalException(BaseResponseCode.OPERATION_ERROR);
        }
    }

    @Override
    public void userUpdatePwd(UserUpdatePwdReqVo vo, String accessToken, String refreshToken) {

        //通过accessToken拿到当前的登录用户信息实体
        String userId = JWTTokenUtil.getUserId(accessToken);

        //校验旧密码
        SysUser sysUser = userMapper.selectByPrimaryKey(userId);
        if (sysUser == null){
            throw new GlobalException(BaseResponseCode.DATA_ERROR);
        }
        //如果校验不正确
        if (!PasswordUtils.matches(sysUser.getSalt(),vo.getOldPwd(),sysUser.getPassword())){
            throw new GlobalException(BaseResponseCode.OLD_PASSWORD_ERROR);
        }

        //保存新密码
        sysUser.setUpdateTime(new Date());
        sysUser.setUpdateId(userId);
        sysUser.setPassword(PasswordUtils.encode(vo.getNewPwd(),sysUser.getSalt()));
        int i = userMapper.updateByPrimaryKeySelective(sysUser);
        if (i !=1){
            throw new GlobalException(BaseResponseCode.OPERATION_ERROR);
        }

        //把token加入黑名单，防止再次访问
        redisUtil.set(Constant.JWT_ACCESS_TOKEN_BLACKLIST+accessToken,userId,JWTTokenUtil.getRemainingTime(accessToken),TimeUnit.MILLISECONDS);

        //把refreshToken加入黑名单 ， 防止再次拿来刷新token
        redisUtil.set(Constant.JWT_ACCESS_TOKEN_BLACKLIST+refreshToken,userId,JWTTokenUtil.getRemainingTime(refreshToken),TimeUnit.MILLISECONDS);

        //清除用户授权数据缓存
        redisUtil.delete(Constant.IDENTIFY_CACHE_KEY+userId);
    }

    //mock数据
    private List<String> getRoleByUserId(String userId) {
//        List<String> list = new ArrayList<>();
//
//
//        if (userId.equals("fcf34b56-a7a2-4719-9236-867495e74c31")) {
//
//            list.add("admin");
//        } else {
//            list.add("dev");
//        }
//        return list;
        //返回这个用户拥有的角色信息
        return roleService.getNamesByUserId(userId);
    }

    private List<String> getPermissionByUserId(String userId) {
//        List<String> list = new ArrayList<>();
//
//        if (userId.equals("fcf34b56-a7a2-4719-9236-867495e74c31")) {
//            list.add("sys:user:add");
//            list.add("sys:user:update");
//            list.add("sys:user:delete");
//            list.add("sys:user:list");
//        } else {
//
////            list.add("sys:user:list");
//            list.add("sys:user:add");
//        }
        //返回这个用户拥有的权限集合
        return permissionService.getPermissionByUserId(userId);
    }


}
